/**
 * 获取媒体设备列表
 */
const getDevicesList = (kind) => {
  let result = []
  if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
    return navigator.mediaDevices.enumerateDevices().then((deviceInfos) => {
      if (kind) {
        result = deviceInfos.filter(v => v.kind === kind)
      } else {
        result = deviceInfos
      }
      return result
    })
  } else {
    return Promise.resolve(result)
  }
}

/**
 * 请求媒体权限
 */
const getMediaPermission = (constraints) => {
  constraints = constraints || {video: true, audio: true}
  return new Promise((resolve) => {
    // 尝试获取媒体流
    navigator.mediaDevices.getUserMedia(constraints).then(() => {
      resolve(true); // 用户授予了媒体权限
    }).catch((error) => {
      resolve(false); // 用户没有授予媒体权限
    });
  });
}

/**
 * 调用设备 - 失败
 */
const getStreamError = (err, callback) => {
  let message = ''
  if (err.name === 'NotFoundError' || err.name === 'DevicesNotFoundError') {
    message = '未检测到可用设备，请连接设备后重试'
  } else if (err.name === 'NotReadableError' || err.name === 'TrackStartError' || err.name === 'SourceUnavailableError') {
    message = '检测到设备正被占用，请稍后重试'
  } else if (err.name === 'OverconstrainedError' || err.name === 'ConstraintNotSatisfiedError') {
    message = '检测到设备参数无法满足功能需求'
  } else if (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError') {
    message = '调用设备权限被拒绝'
  } else if (err.name === 'TypeError') {
    message = '设备参数要求未设置'
  } else {
    message = `访问用户媒体设备失败：${err.name}, ${err.message}`
  }
  console.log(message)
  callback && callback(message, err)
}

/**
 * 关闭数据流
 */
const closeStream = (mediaStream) => {
  if (!mediaStream) return
  if (mediaStream.stop) {
    mediaStream.stop()
  } else { // Chrome 45版本以上关闭方式
    let track = mediaStream.getTracks()[0]
    track.stop && track.stop()
  }
}

/**
 * 获取摄像头列表&获取摄像头视频流&获取视频流错误处理&关闭视频流
 * @param {Object} constraints getStream/媒体参数配置
 * @param {String} deviceId getStream/媒体设备ID
 * @param {Object} mediaDom getStream/video标签DOM元素
 * @param {Function} success getStream/成功回调函数
 * @param {Function} fail getStream/失败回调函数
 * @param {Function} oninactive/onended getStream/数据流结束回调函数
 * @param {Object} err getStreamError/错误对象
 * @param {Function} callback getStreamError/回调函数
 * @param {Object} mediaStream closeStream/媒体流对象
 */
const myCamera = {
  // 摄像头视频流
  stream: null,
  // 获取摄像头列表
  getDevicesList: function () {
    return getDevicesList('videoinput')
  },
  // 调用设备 - 失败
  getStreamError,
  // 获取数据流
  getStream: function (options) {
    options = options || {}
    // if (this.stream) {
    //   this.closeStream(this.stream)
    //   this.stream = null
    // }
    let constraints = options.constraints || {video: {}, audio: false}
    let deviceId = options.deviceId || ''
    let mediaDom = options.mediaDom
    let oninactive = options.oninactive || options.onended || (() => {console.log('数据流关闭')})
    let success = (mediaStream) => {
      // 监听数据流关闭
      mediaStream.oninactive = oninactive
      this.stream = mediaStream
      let tracks = []
      if (mediaStream.getTracks) { tracks = mediaStream.getTracks() }
      else if (mediaStream.getVideoTracks) { tracks = mediaStream.getVideoTracks() }
      tracks.forEach(v => (v.onended = oninactive))
      mediaDom && (mediaDom.autoplay = 'autoplay')
      try {
        mediaDom && (mediaDom.src = (window.URL || window.webkitURL).createObjectURL(mediaStream))
      } catch (err) {
        mediaDom && (mediaDom.srcObject = mediaStream)
      }
      options.success && options.success(mediaStream)
    }
    let fail = (err) => {
      this.getStreamError(err, options.fail)
    }
    if (deviceId) {
      if (!constraints.video) constraints.video = {}
      if (!constraints.video.deviceId) constraints.video.deviceId = {}
      constraints.video.deviceId.exact = deviceId
    }
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      return navigator.mediaDevices.getUserMedia(constraints).then(success).catch(fail)
    } else if (navigator.webkitGetUserMedia) {
      return navigator.webkitGetUserMedia(constraints).then(success).catch(fail)
    } else if (navigator.mozGetUserMedia) {
      return navigator.mozGetUserMedia(constraints).then(success).catch(fail)
    } else if (navigator.msGetUserMedia) {
      return navigator.msGetUserMedia(constraints).then(success).catch(fail)
    } else if (navigator.getUserMedia) {
      return navigator.getUserMedia(constraints).then(success).catch(fail)
    } else {
      return Promise.reject('你的浏览器不支持访问用户媒体设备，请更换浏览器后重试')
    }
  },
  // 关闭数据流
  closeStream: function (mediaStream) {
    mediaStream = mediaStream || this.stream
    closeStream(mediaStream)
  }
}
class Camera {
  deviceId = '' // 摄像头id
  constraints = {}
  stream = null // 摄像头视频流
  mediaDom = null
  constructor (options) {
    options = options || {};
    this.constraints = options.constraints || {video: {}, audio: false}
    this.deviceId = options.deviceId || ''
    this.mediaDom = options.mediaDom
    let oninactive = options.oninactive || options.onended || (() => {console.log('数据流关闭')})
    let success = (mediaStream) => {
      // 监听数据流关闭
      mediaStream.oninactive = oninactive
      this.stream = mediaStream
      let tracks = []
      if (mediaStream.getTracks) { tracks = mediaStream.getTracks() }
      else if (mediaStream.getVideoTracks) { tracks = mediaStream.getVideoTracks() }
      tracks.forEach(v => (v.onended = oninactive))
      this.mediaDom && (this.mediaDom.autoplay = 'autoplay')
      try {
        this.mediaDom && (this.mediaDom.src = (window.URL || window.webkitURL).createObjectURL(mediaStream))
      } catch (err) {
        this.mediaDom && (this.mediaDom.srcObject = mediaStream)
      }
      options.success && options.success(mediaStream)
    }
    let fail = (err) => {
      getStreamError(err, options.fail)
    }
    if (this.deviceId) {
      if (!this.constraints.video) this.constraints.video = {}
      if (!this.constraints.video.deviceId) this.constraints.video.deviceId = {}
      this.constraints.video.deviceId.exact = this.deviceId
    }
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      return navigator.mediaDevices.getUserMedia(this.constraints).then(success).catch(fail)
    } else if (navigator.webkitGetUserMedia) {
      return navigator.webkitGetUserMedia(this.constraints).then(success).catch(fail)
    } else if (navigator.mozGetUserMedia) {
      return navigator.mozGetUserMedia(this.constraints).then(success).catch(fail)
    } else if (navigator.msGetUserMedia) {
      return navigator.msGetUserMedia(this.constraints).then(success).catch(fail)
    } else if (navigator.getUserMedia) {
      return navigator.getUserMedia(this.constraints).then(success).catch(fail)
    } else {
      return Promise.reject('你的浏览器不支持访问用户媒体设备，请更换浏览器后重试')
    }
  }
  // 获取摄像头列表
  static getDevicesList () {
    return getDevicesList('videoinput')
  }
  // 获取数据流
  getStream () {
    return this.stream
  }
}

/**
 * 获取录音设备列表&获取录音设备数据流&获取音频流错误处理&关闭音频流
 * @param {Object} constraints getStream/媒体参数配置
 * @param {String} deviceId getStream/媒体设备ID
 * @param {Object} mediaDom getStream/audio标签DOM元素
 * @param {Function} success getStream/成功回调函数
 * @param {Function} fail getStream/失败回调函数
 * @param {Function} oninactive/onended getStream/数据流结束回调函数
 * @param {Object} err getStreamError/错误对象
 * @param {Function} callback getStreamError/回调函数
 * @param {Object} mediaStream closeStream/媒体流对象
 */
const myRecorder = {
  // 音频数据流
  stream: null,
  // 录音器实例
  mediaRecorder: null,
  // 录音数据存储
  chunks: [],
  // 录音结束后生成的URL地址
  mediaUrl: '',
  // 获取录音设备列表
  getDevicesList: function () {
    return getDevicesList('audioinput')
  },
  // 调用设备 - 失败
  getStreamError,
  // 获取数据流
  getStream: function (options) {
    options = options || {}
    // if (this.stream) {
    //   this.closeStream(this.stream)
    //   this.stream = null
    // }
    let constraints = options.constraints || {audio: true}
    let deviceId = options.deviceId || ''
    let mediaDom = options.mediaDom
    let oninactive = options.oninactive || options.onended || (() => {console.log('数据流关闭')})
    let success = (mediaStream) => {
      // 监听数据流关闭
      mediaStream.oninactive = oninactive
      this.stream = mediaStream
      let tracks = []
      if (mediaStream.getTracks) { tracks = mediaStream.getTracks() }
      else if (mediaStream.getAudioTracks) { tracks = mediaStream.getAudioTracks() }
      tracks.forEach(v => (v.onended = oninactive))
      mediaDom && (mediaDom.autoplay = 'autoplay')
      try {
        mediaDom && (mediaDom.src = (window.URL || window.webkitURL).createObjectURL(mediaStream))
      } catch (err) {
        mediaDom && (mediaDom.srcObject = mediaStream)
      }
      options.success && options.success(mediaStream)
    }
    let fail = (err) => {
      this.getStreamError(err, options.fail)
    }
    if (deviceId) {
      if (!constraints.audio) constraints.audio = {}
      if (!constraints.audio.deviceId) constraints.audio.deviceId = {}
      constraints.audio.deviceId.exact = deviceId
    }
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      return navigator.mediaDevices.getUserMedia(constraints).then(success).catch(fail)
    } else if (navigator.webkitGetUserMedia) {
      return navigator.webkitGetUserMedia(constraints).then(success).catch(fail)
    } else if (navigator.mozGetUserMedia) {
      return navigator.mozGetUserMedia(constraints).then(success).catch(fail)
    } else if (navigator.msGetUserMedia) {
      return navigator.msGetUserMedia(constraints).then(success).catch(fail)
    } else if (navigator.getUserMedia) {
      return navigator.getUserMedia(constraints).then(success).catch(fail)
    } else {
      return Promise.reject('你的浏览器不支持访问用户媒体设备，请更换浏览器后重试')
    }
  },
  // 关闭数据流
  closeStream: function (mediaStream) {
    mediaStream = mediaStream || this.stream
    closeStream(mediaStream)
  },
  // 获取录音器实例
  getMediaRecorder: function (options = {}) {
    options = options || {}
    let mediaStream = options.mediaStream || this.stream
    if (mediaStream) {
      this.initMediaRecorder({...options, mediaStream})
      return Promise.resolve(this.mediaRecorder)
    } else {
      return this.getStream().then(() => {
        mediaStream = this.stream
        this.initMediaRecorder({...options, mediaStream})
        return this.mediaRecorder
      })
    }
  },
  // 初始化录音器实例
  initMediaRecorder: function (options) {
    options = options || {}
    let _this = this
    let mediaStream = options.mediaStream || this.stream
    let start = options.start || (() => {console.log('开始录音')})
    let stop = options.stop || (() => {console.log('录音结束')})
    let dataavailable = options.dataavailable || (() => {console.log('获取录制的媒体资源')})
    this.chunks = []
    this.mediaUrl = ''
    this.mediaRecorder = new MediaRecorder(mediaStream)
    this.mediaRecorder.ondataavailable = function (e) {
      _this.chunks.push(e.data)
      dataavailable(e)
    }
    this.mediaRecorder.onstart = function (e) {
      start(e)
    }
    this.mediaRecorder.onstop = function (e) {
      let blob = new Blob(_this.chunks, {type: 'audio/mp3'})
      let url = window.URL.createObjectURL(blob)
      _this.mediaUrl = url
      stop(e)
      return url
    }
  },
  stop() {
    this.mediaRecorder && this.mediaRecorder.stop()
    this.closeStream()
    this.stream = null
    this.mediaRecorder = null
  },
  reset() {
    this.closeStream()
    this.stream = null
    this.mediaRecorder = null
    this.chunks = []
    this.mediaUrl = ''
  }
}

module.exports = {
  getMediaPermission,
  Camera,
  myCamera,
  myRecorder
}
